/**
 * @file stopwatch.c
 *
 */

/*********************
*      INCLUDES
*********************/
#include "lv_watch.h"

#if USE_LV_WATCH_STOPWATCH != 0

#include <stdio.h>

/*********************
*      DEFINES
*********************/

/**********************
*      TYPEDEFS
**********************/

/**********************
*  STATIC PROTOTYPES
**********************/
static void stopwatch_prepare_destory(lv_obj_t * activity_obj);
static lv_res_t stopwatch_img_start_signal(lv_obj_t * img, lv_signal_t sign, void * param);
static lv_res_t stopwatch_img_pause_signal(lv_obj_t * img, lv_signal_t sign, void * param);
static lv_res_t stopwatch_img_reset_signal(lv_obj_t * img, lv_signal_t sign, void * param);
static bool stopwatch_display_time(lv_stopwatch_ext_t * ext);
static void stopwatch_period_update(lv_obj_t * obj);
static uint32_t stopwatch_tick_to_mseconds(uint32_t tick);

/**********************
*  STATIC VARIABLES
**********************/
static uint32_t sw_tick_start = 0; /*in millisecond*/
static uint32_t sw_tick_end   = 0; /*in millisecond*/
static uint32_t saved_msecond = 0;

enum {
    STOPWATCH_STATE_IDLE,
    STOPWATCH_STATE_RUN,
    STOPWATCH_STATE_PAUSE
};
typedef uint8_t STOPWATCH_STATE;
static STOPWATCH_STATE sw_state = STOPWATCH_STATE_IDLE;
static lv_signal_cb_t old_signal_func;

/**********************
*      MACROS
**********************/

/**********************
 *   GLOBAL FUNCTIONS
 ***********************/
lv_obj_t * stopwatch_create(lv_obj_t * activity_obj)
{
    if(activity_obj == NULL) {
        lv_watch_activity_ext_t activity_ext;
        memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
        activity_ext.actId = ACT_ID_STOPWATCH;
        activity_ext.create = stopwatch_create;
        activity_ext.prepare_destory = stopwatch_prepare_destory;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
        LV_ASSERT_MEM(activity_obj);
        if(activity_obj == NULL) {
            return(NULL);
        }
    }

    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    LV_ASSERT_MEM(obj);
    if(obj == NULL) {
        return(NULL);
    }

    lv_obj_t * img = lv_img_create(obj, NULL);
    lv_img_set_src(img, ICON_STOPWATCH_BACKGROUND);
    lv_stopwatch_ext_t * img_ext = lv_obj_allocate_ext_attr(img, sizeof(lv_stopwatch_ext_t));
    img_ext->label_hour_1  = NULL;
    img_ext->label_hour_2  = NULL;
    img_ext->label_colon_1 = NULL;
    lv_coord_t height = lv_obj_get_height(img);

    img_ext->label_title = lv_label_create(img, NULL);
    lv_label_set_long_mode(img_ext->label_title, LV_LABEL_LONG_EXPAND);
    lv_label_set_align(img_ext->label_title, LV_LABEL_ALIGN_CENTER);
    if(STOPWATCH_STATE_IDLE == sw_state) {
        lv_label_set_text_id(img_ext->label_title, WATCH_TEXT_ID_STOPWATCH);
    } else if(STOPWATCH_STATE_RUN == sw_state) {
        lv_label_set_text_id(img_ext->label_title, WATCH_TEXT_ID_TIMING);
    } else if(STOPWATCH_STATE_PAUSE == sw_state) {
        lv_label_set_text_id(img_ext->label_title, WATCH_TEXT_ID_PAUSE);
    }
    lv_obj_align(img_ext->label_title, img, LV_ALIGN_IN_TOP_MID, 0, height / 5);

    lv_obj_t * cont_time = lv_cont_create(img, NULL);
    lv_obj_set_style_local_bg_opa(cont_time, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_obj_set_style_local_border_opa(cont_time, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_0);
    lv_cont_set_fit(cont_time, true);
    lv_cont_set_layout(cont_time, LV_LAYOUT_ROW_BOTTOM);
    lv_obj_set_click(cont_time, false);

    lv_bidi_dir_t dir = lv_obj_get_base_dir(cont_time);
    if(LV_BIDI_DIR_RTL == dir) {
        lv_obj_set_base_dir(cont_time, LV_BIDI_DIR_LTR);
    }

    img_ext->label_min_1 = lv_label_create(cont_time, NULL);
    lv_obj_add_style(img_ext->label_min_1, LV_LABEL_PART_MAIN, &lv_watch_font40);
    lv_label_set_long_mode(img_ext->label_min_1, LV_LABEL_LONG_BREAK);
    lv_obj_set_width(img_ext->label_min_1, STOPWATCH_LABEL_NON_MS_WIDTH);
    lv_label_set_align(img_ext->label_min_1, LV_LABEL_ALIGN_CENTER);
    img_ext->label_min_2 = lv_label_create(cont_time, img_ext->label_min_1);
    img_ext->label_colon_2 = lv_label_create(cont_time, img_ext->label_min_1);
    lv_obj_set_width(img_ext->label_colon_2, STOPWATCH_LABEL_COLON_WIDTH);
    img_ext->label_sec_1 = lv_label_create(cont_time, img_ext->label_min_1);
    img_ext->label_sec_2 = lv_label_create(cont_time, img_ext->label_min_1);
    img_ext->label_ms_1 = lv_label_create(cont_time, NULL);
    lv_obj_add_style(img_ext->label_ms_1, LV_LABEL_PART_MAIN, &lv_watch_font30);
    lv_label_set_long_mode(img_ext->label_ms_1, LV_LABEL_LONG_BREAK);
    lv_obj_set_width(img_ext->label_ms_1, STOPWATCH_LABEL_MS_WIDTH + STOPWATCH_LABEL_SPACE_WIDTH);
    lv_label_set_align(img_ext->label_ms_1, LV_LABEL_ALIGN_RIGHT);
    img_ext->label_ms_2 = lv_label_create(cont_time, img_ext->label_ms_1);
    lv_obj_set_width(img_ext->label_ms_2, STOPWATCH_LABEL_MS_WIDTH);
    lv_label_set_align(img_ext->label_ms_2, LV_LABEL_ALIGN_CENTER);
    lv_label_set_text(img_ext->label_colon_2, ":");

    sw_tick_end = 0;
    if(STOPWATCH_STATE_RUN == sw_state) {
        sw_tick_end = Hal_Timer_Get_Counter();
    } else {
        sw_tick_start = 0;
    }

    if(false == stopwatch_display_time(img_ext)) {
        lv_obj_align(cont_time, img, LV_ALIGN_CENTER, 0, 0);
    }

    img_ext->img_btn_start_pause = lv_img_create(img, NULL);
    lv_img_set_auto_size(img_ext->img_btn_start_pause, true);
    lv_obj_set_click(img_ext->img_btn_start_pause, true);
    old_signal_func = lv_obj_get_signal_cb(img_ext->img_btn_start_pause);
    img_ext->img_btn_reset = NULL;
    if(STOPWATCH_STATE_IDLE == sw_state) {
        lv_img_set_src(img_ext->img_btn_start_pause, ICON_STOPWATCH_START);
        lv_obj_set_signal_cb(img_ext->img_btn_start_pause, stopwatch_img_start_signal);
        lv_obj_align(img_ext->img_btn_start_pause, img, LV_ALIGN_IN_TOP_MID, 0, height * 2 / 3);
    } else if(STOPWATCH_STATE_RUN == sw_state) {
        lv_img_set_src(img_ext->img_btn_start_pause, ICON_STOPWATCH_PAUSE);
        lv_obj_set_signal_cb(img_ext->img_btn_start_pause, stopwatch_img_pause_signal);
        lv_obj_align(img_ext->img_btn_start_pause, img, LV_ALIGN_IN_TOP_MID, 0, height * 2 / 3);

        stopwatch_period_update(img);
    } else if(STOPWATCH_STATE_PAUSE == sw_state) {
        lv_img_set_src(img_ext->img_btn_start_pause, ICON_STOPWATCH_START);
        lv_obj_set_signal_cb(img_ext->img_btn_start_pause, stopwatch_img_start_signal);
        lv_coord_t width = lv_obj_get_width(img);

        img_ext->img_btn_reset = lv_img_create(img, img_ext->img_btn_start_pause);
        lv_img_set_src(img_ext->img_btn_reset, ICON_STOPWATCH_RESET);
        lv_obj_set_signal_cb(img_ext->img_btn_reset, stopwatch_img_reset_signal);

        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_align(img_ext->img_btn_reset, img, LV_ALIGN_IN_TOP_RIGHT, width / (-4),
                         height * 2 / 3);
            lv_obj_align(img_ext->img_btn_start_pause, img, LV_ALIGN_IN_TOP_LEFT, width / 4,
                         height * 2 / 3);
        } else {
            lv_obj_align(img_ext->img_btn_start_pause, img, LV_ALIGN_IN_TOP_RIGHT, width / (-4),
                         height * 2 / 3);
            lv_obj_align(img_ext->img_btn_reset, img, LV_ALIGN_IN_TOP_LEFT, width / 4,
                         height * 2 / 3);
        }
    }

    return(obj);
} /* stopwatch_create */

void stopwatch_create_event_cb(lv_obj_t * btn, lv_event_t e)
{
    (void)btn;
    if(LV_EVENT_CLICKED == e)
    {
        lv_watch_png_cache_all_free();
        lv_obj_t * obj = stopwatch_create(NULL);
        LV_ASSERT_MEM(obj);
        if(obj == NULL) return;

        watch_set_suspend_enable(false, ACT_ID_STOPWATCH, 1);
    }
}

/**********************
*   STATIC FUNCTIONS
**********************/
static void stopwatch_prepare_destory(lv_obj_t * activity_obj)
{
    watch_set_suspend_enable(true, ACT_ID_STOPWATCH, 1);
    lv_watch_png_cache_all_free();
}

static bool stopwatch_display_time(lv_stopwatch_ext_t * ext)
{
    uint32_t tick;
    uint32_t mseconds = 0;

    if(sw_tick_start > sw_tick_end) {
        tick = 0xFFFFFFFF - sw_tick_start + sw_tick_end + saved_msecond;
    } else {
        tick = sw_tick_end - sw_tick_start + saved_msecond;
    }

    if (0 != tick)
        mseconds = stopwatch_tick_to_mseconds(tick);

    uint8_t msec = (mseconds / 10) % 100;          /*[0,99]*/
    uint8_t sec  = (mseconds / 1000) % 60;         /*[0,59]*/
    uint8_t min  = (mseconds / 1000 / 60) % 60;    /*[0,59]*/
    uint8_t hour = (mseconds / 1000 / 3600) % 100; /*[0,99]*/

    char str[2] = {0};
    str[0] = min / 10 + '0';
    lv_label_set_text(ext->label_min_1, str);
    str[0] = min % 10 + '0';
    lv_label_set_text(ext->label_min_2, str);

    str[0] = sec / 10 + '0';
    lv_label_set_text(ext->label_sec_1, str);
    str[0] = sec % 10 + '0';
    lv_label_set_text(ext->label_sec_2, str);

    str[0] = msec / 10 + '0';
    lv_label_set_text(ext->label_ms_1, str);
    str[0] = msec % 10 + '0';
    lv_label_set_text(ext->label_ms_2, str);

    if(0 != hour) {
        if(NULL == ext->label_hour_1) {
            lv_obj_t * cont_time = lv_obj_get_parent(ext->label_min_1);
            ext->label_colon_1 = lv_label_create(cont_time, ext->label_colon_2);
            ext->label_hour_1 = lv_label_create(cont_time, ext->label_min_1);
            ext->label_hour_2 = lv_label_create(cont_time, ext->label_min_1);
            lv_obj_move_background(ext->label_colon_1);
            lv_obj_move_background(ext->label_hour_2);
            lv_obj_move_background(ext->label_hour_1);
            lv_label_set_text(ext->label_colon_1, ":");
            lv_obj_set_width(ext->label_ms_1, STOPWATCH_LABEL_MS_WIDTH);
            lv_obj_align(cont_time, lv_obj_get_parent(cont_time), LV_ALIGN_CENTER, 0, 0);
        }
        str[0] = hour / 10 + '0';
        lv_label_set_text(ext->label_hour_1, str);
        str[0] = hour % 10 + '0';
        lv_label_set_text(ext->label_hour_2, str);
    } else {
        if (ext->label_hour_1) {
            lv_obj_t * cont_time = lv_obj_get_parent(ext->label_min_1);
            lv_obj_del(ext->label_hour_1);
            ext->label_hour_1 = NULL;
            lv_obj_del(ext->label_hour_2);
            ext->label_hour_2 = NULL;
            lv_obj_del(ext->label_colon_1);
            ext->label_colon_1 = NULL;
            lv_obj_set_width(ext->label_ms_1, STOPWATCH_LABEL_MS_WIDTH + STOPWATCH_LABEL_SPACE_WIDTH);
            lv_obj_align(cont_time, lv_obj_get_parent(cont_time), LV_ALIGN_CENTER, 0, 0);
        } else {
            return(false);
        }
    }

    return(true);
}

static void stopwatch_time_update(lv_anim_t * anim)
{
    lv_stopwatch_ext_t * ext = (lv_stopwatch_ext_t *)lv_obj_get_ext_attr(anim->var);

    sw_tick_end = Hal_Timer_Get_Counter();

    stopwatch_display_time(ext);
}

static void stopwatch_period_update(lv_obj_t * obj)
{
    lv_anim_path_t path;
    lv_anim_path_init(&path);
    lv_anim_path_set_cb(&path, lv_anim_path_linear);
    lv_anim_t a = {};
    lv_anim_init(&a);
    lv_anim_set_var(&a, obj);
    lv_anim_set_start_cb(&a, (lv_anim_ready_cb_t)stopwatch_time_update);
    lv_anim_set_time(&a, 0);
    lv_anim_set_repeat_delay(&a, 29);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_set_path(&a, &path);
    lv_anim_start(&a);
}

static lv_res_t stopwatch_img_start_signal(lv_obj_t * img, lv_signal_t sign, void * param)
{
    lv_res_t res = LV_RES_OK;

    res = old_signal_func(img, sign, param);

    if(sign == LV_SIGNAL_RELEASED) {
        lv_obj_t * par = lv_obj_get_parent(img);
        lv_stopwatch_ext_t * par_ext = (lv_stopwatch_ext_t *)lv_obj_get_ext_attr(par);
        lv_coord_t height = lv_obj_get_height(par);

        lv_label_set_text_id(par_ext->label_title, WATCH_TEXT_ID_TIMING);
        lv_obj_align(par_ext->label_title, par, LV_ALIGN_IN_TOP_MID, 0, height / 5);

        if(NULL != par_ext->img_btn_reset) {
            lv_obj_del(par_ext->img_btn_reset);
            par_ext->img_btn_reset = NULL;
        }

        lv_img_set_src(par_ext->img_btn_start_pause, ICON_STOPWATCH_PAUSE);
        lv_obj_set_signal_cb(par_ext->img_btn_start_pause, stopwatch_img_pause_signal);
        lv_obj_align(par_ext->img_btn_start_pause, par, LV_ALIGN_IN_TOP_MID, 0, height * 2 / 3);

        lv_obj_set_state(par_ext->img_btn_start_pause, LV_STATE_DEFAULT);

        sw_tick_start = Hal_Timer_Get_Counter();
        stopwatch_period_update(par);
        sw_state = STOPWATCH_STATE_RUN;
    }

    return(res);
} /* stopwatch_img_start_signal */

static lv_res_t stopwatch_img_pause_signal(lv_obj_t * img, lv_signal_t sign, void * param)
{
    lv_res_t res = LV_RES_OK;

    res = old_signal_func(img, sign, param);

    if(sign == LV_SIGNAL_RELEASED) {
        lv_obj_t * par = lv_obj_get_parent(img);
        lv_stopwatch_ext_t * par_ext = (lv_stopwatch_ext_t *)lv_obj_get_ext_attr(par);
        lv_coord_t width = lv_obj_get_width(par);
        lv_coord_t height = lv_obj_get_height(par);

        lv_anim_del(par, NULL);

        lv_label_set_text_id(par_ext->label_title, WATCH_TEXT_ID_PAUSE);

        lv_img_set_src(par_ext->img_btn_start_pause, ICON_STOPWATCH_START);
        lv_obj_set_signal_cb(par_ext->img_btn_start_pause, stopwatch_img_start_signal);

        par_ext->img_btn_reset = lv_img_create(par, par_ext->img_btn_start_pause);
        lv_img_set_src(par_ext->img_btn_reset, ICON_STOPWATCH_RESET);
        lv_obj_set_signal_cb(par_ext->img_btn_reset, stopwatch_img_reset_signal);

        if(LV_BIDI_DIR_RTL == lv_obj_get_base_dir(img)) {
            lv_obj_align(par_ext->img_btn_reset, par, LV_ALIGN_IN_TOP_RIGHT, width / (-4),
                         height * 2 / 3);
            lv_obj_align(par_ext->img_btn_start_pause, par, LV_ALIGN_IN_TOP_LEFT, width / 4,
                         height * 2 / 3);
        } else {
            lv_obj_align(par_ext->img_btn_start_pause, par, LV_ALIGN_IN_TOP_RIGHT, width / (-4),
                         height * 2 / 3);
            lv_obj_align(par_ext->img_btn_reset, par, LV_ALIGN_IN_TOP_LEFT, width / 4,
                         height * 2 / 3);
        }

        sw_state = STOPWATCH_STATE_PAUSE;
        sw_tick_end = Hal_Timer_Get_Counter();
        stopwatch_display_time(par_ext);
        if(sw_tick_start > sw_tick_end) {
            saved_msecond += 0xFFFFFFFF - sw_tick_start + sw_tick_end;
        } else {
            saved_msecond += sw_tick_end - sw_tick_start;
        }
    }
    return(res);
} /* stopwatch_img_pause_signal */

static lv_res_t stopwatch_img_reset_signal(lv_obj_t * img, lv_signal_t sign, void * param)
{
    lv_res_t res = LV_RES_OK;

    res = old_signal_func(img, sign, param);

    if(sign == LV_SIGNAL_RELEASED) {
        lv_obj_t * par = lv_obj_get_parent(img);
        lv_stopwatch_ext_t * par_ext = (lv_stopwatch_ext_t *)lv_obj_get_ext_attr(par);
        lv_coord_t height = lv_obj_get_height(par);
        lv_label_set_text_id(par_ext->label_title, WATCH_TEXT_ID_STOPWATCH);
        lv_obj_align(par_ext->label_title, par, LV_ALIGN_IN_TOP_MID, 0, height / 5);

        sw_tick_start = 0;
        sw_tick_end = 0;
        saved_msecond = 0;
        stopwatch_display_time(par_ext);

        lv_obj_del(par_ext->img_btn_reset);
        par_ext->img_btn_reset = NULL;

        lv_obj_align(par_ext->img_btn_start_pause, par, LV_ALIGN_IN_TOP_MID, 0, height * 2 / 3);

        sw_state = STOPWATCH_STATE_IDLE;
    }

    return(res);
} /* stopwatch_img_reset_signal */

static uint32_t stopwatch_tick_to_mseconds(uint32_t tick)
{
    return((tick / MICRO_SEC_TO_CLOCK_TICKS_CLK_32KHZ(0xFFFFFFFF)) * (0xFFFFFFFF / 1000)
           + CLOCK_TICKS_TO_MICRO_SEC_CLK_32KHZ(tick) / 1000);
}

#endif
